1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package de.bea.domingo.connector.impl;
24
25 import java.io.PrintWriter;
26 import java.util.Collections;
27 import java.util.HashSet;
28 import java.util.Iterator;
29 import java.util.Set;
30
31 import javax.resource.NotSupportedException;
32 import javax.resource.ResourceException;
33 import javax.resource.cci.Connection;
34 import javax.resource.spi.ConnectionEvent;
35 import javax.resource.spi.ConnectionEventListener;
36 import javax.resource.spi.ConnectionRequestInfo;
37 import javax.resource.spi.LocalTransaction;
38 import javax.resource.spi.ManagedConnection;
39 import javax.resource.spi.ManagedConnectionMetaData;
40 import javax.resource.spi.security.GenericCredential;
41 import javax.resource.spi.security.PasswordCredential;
42 import javax.security.auth.Subject;
43 import javax.transaction.xa.XAResource;
44
45 import de.bea.domingo.DNotesException;
46 import de.bea.domingo.DNotesFactory;
47 import de.bea.domingo.DNotesRuntimeException;
48 import de.bea.domingo.DSession;
49 import de.bea.domingo.connector.DomingoConnection;
50 import de.bea.domingo.i18n.ResourceManager;
51 import de.bea.domingo.i18n.Resources;
52
53 /***
54 * @author <a href=mailto:kurt.riede@bea.de>Kurt Riede</a>
55 */
56 public final class DomingoManagedConnection implements ManagedConnection, ManagedConnectionMetaData {
57
58 /*** Internationalized resources. */
59 protected static final Resources RESOURCES = ResourceManager.getPackageResources(DomingoManagedConnection.class);
60
61 /*** Internationalized resources. */
62 protected static final Resources METADATA = ResourceManager.getClassResources(DomingoManagedConnection.class);
63
64 /*** Reference to a Domingo monitor for logging. */
65 private DomingoLogAdapter monitor;
66
67 /*** Reference to the domingo factory. */
68 private DNotesFactory factory;
69
70 /*** Reference to the domingo session. */
71 private DSession session;
72
73 /*** Set of event listeners. */
74 private Set eventListeners = Collections.synchronizedSet(new HashSet());
75
76 /*** Set of all available domingo connections. */
77 private Set connections = Collections.synchronizedSet(new HashSet());
78
79 /*** Flag if the connection is already destroyed or not. */
80 private boolean isDestroyed;
81
82 private GenericCredential genericCredential;
83 private PasswordCredential passwordCredential;
84 private boolean hasGenericCred;
85 private boolean hasPasswordCred;
86 private ConnectionRequestInfo connectionRequestInfo;
87 private DomingoManagedConnectionFactory managedConnectionFactory;
88 private boolean destroyed;
89
90 /***
91 * Constructor (using only ConnectionRequestInfo).
92 *
93 * @param theMonitor the domingo monitor to use for monitoring
94 * @param mcf the managed connection factory that creates this connection
95 * @param cri connection request information for authentication
96 * @throws ResourceException if the connection cannot be created
97 */
98 public DomingoManagedConnection(final DomingoLogAdapter theMonitor, final DomingoManagedConnectionFactory mcf,
99 final ConnectionRequestInfo cri) throws ResourceException {
100 monitor = theMonitor;
101 managedConnectionFactory = mcf;
102 connectionRequestInfo = cri;
103 hasGenericCred = false;
104 hasPasswordCred = false;
105 try {
106 session = createPhysicalConnection(connectionRequestInfo);
107 } catch (Exception e) {
108 throw new ResourceException(RESOURCES.getString("cannot.instantiate.managed.connection"), e);
109 }
110 }
111
112 /***
113 * Constructor (using PasswordCredential).
114 *
115 * @param theMonitor the domingo monitor to use for monitoring
116 * @param mcf the managed connection factory that creates this connection
117 * @param pc the password credentials for authentication
118 * @throws ResourceException if the connection cannot be created
119 */
120 public DomingoManagedConnection(final DomingoLogAdapter theMonitor, final DomingoManagedConnectionFactory mcf,
121 final PasswordCredential pc) throws ResourceException {
122 monitor = theMonitor;
123 managedConnectionFactory = mcf;
124 passwordCredential = pc;
125 connectionRequestInfo = null;
126 hasPasswordCred = true;
127 hasGenericCred = false;
128 try {
129 String username = pc.getUserName();
130 char[] passwd = pc.getPassword();
131 session = createPhysicalConnection(username, passwd);
132 } catch (Exception e) {
133 throw new ResourceException(RESOURCES.getString("cannot.instantiate.managed.connection"), e);
134 }
135 }
136
137 /***
138 * Constructor (using GenericCredentials).
139 *
140 * @param theMonitor the domingo monitor to use for monitoring
141 * @param mcf the managed connection factory that creates this connection
142 * @param gc generic credentials for authentication
143 * @throws ResourceException if the connection cannot be created
144 */
145 public DomingoManagedConnection(final DomingoLogAdapter theMonitor, final DomingoManagedConnectionFactory mcf,
146 final GenericCredential gc) throws ResourceException {
147 monitor = theMonitor;
148 managedConnectionFactory = mcf;
149 genericCredential = gc;
150 connectionRequestInfo = null;
151 hasGenericCred = true;
152 hasPasswordCred = false;
153 try {
154 session = createPhysicalConnection(gc);
155 } catch (Exception e) {
156 throw new ResourceException(RESOURCES.getString("cannot.instantiate.managed.connection"), e);
157 }
158 }
159
160 /***
161 * Returns the factorz of a managed connection.
162 *
163 * @return factory
164 */
165 DomingoManagedConnectionFactory getManagedConnectionFactory() {
166 return this.managedConnectionFactory;
167 }
168
169 /***
170 * Returns a physical connection (using only ConnectionRequestInfo).
171 *
172 * @param cri connection request information for authentication
173 * @return physical connection to the EIS
174 * @throws ResourceException if the connection cannot be created
175 */
176 protected DSession createPhysicalConnection(final ConnectionRequestInfo cri) throws ResourceException {
177 throw new NotSupportedException(RESOURCES.getString("no.authentication"));
178 }
179
180 /***
181 * Returns a physical connection (using GenericCredential).
182 *
183 * @param gc generic credentials for authentication
184 * @return physical connection to the EIS
185 * @throws ResourceException if the connection cannot be created
186 */
187 protected DSession createPhysicalConnection(final GenericCredential gc) throws ResourceException {
188 throw new NotSupportedException(RESOURCES.getString("no.generic.authentication"));
189 }
190
191 /***
192 * Returns a physical connection (using username and password).
193 *
194 * @param username username for authentication
195 * @param password password for authentication
196 * @return physical connection to the EIS
197 * @throws ResourceException if the connection cannot be created
198 */
199 protected DSession createPhysicalConnection(final String username, final char[] password) throws ResourceException {
200 if (connectionRequestInfo != null) {
201
202 throw new UnsupportedOperationException("createPhysicalConnection(String, char[])");
203 }
204 final String host = ((DomingoManagedConnectionFactory) getManagedConnectionFactory()).getHost();
205 final String port = ((DomingoManagedConnectionFactory) getManagedConnectionFactory()).getPort();
206 return createSession(host, port, username, password);
207 }
208
209 /***
210 * Returns the domingo session of a managed connection.
211 *
212 * @return domingo session
213 */
214 public DSession getSession() {
215 return session;
216 }
217
218 /***
219 * Returns the associated monitor.
220 *
221 * @return associated monitor
222 */
223 public DomingoLogAdapter getMonitor() {
224 return monitor;
225 }
226
227 /***
228 * {@inheritDoc}
229 *
230 * @see javax.resource.spi.ManagedConnection#associateConnection(java.lang.Object)
231 */
232 public void associateConnection(final Object connection) throws ResourceException {
233 ((DomingoConnection) connection).reassociate(this);
234 }
235
236 /***
237 * Returns the domingo session of the connection.
238 *
239 * @param host name or IP-address of Lotus Domino server
240 * @param port port of server of Lotus Domino server
241 * @param username username for authentication
242 * @param password password for authentication
243 * @return domingo session
244 * @throws ResourceException if the domingo session cannot be established
245 */
246 private DSession createSession(final String host, final String port, final String username, char[] password)
247 throws ResourceException {
248 monitor.debug("createSession(" + host + ", " + port + ", " + username + ", ********)");
249 if (session == null || !session.isValid()) {
250 try {
251
252 factory = DNotesFactory.getInstance(monitor);
253 session = factory.getSession(host + ":" + port, username, new String(password));
254 } catch (DNotesRuntimeException e) {
255 throw new ResourceException(RESOURCES.getString("cannot.create.session.1", e.getCause().getMessage()), e);
256 }
257 }
258 if (session == null) {
259 throw new ResourceException(RESOURCES.getString("cannot.create.session"));
260 }
261 return session;
262 }
263
264 /***
265 * {@inheritDoc}
266 *
267 * @see javax.resource.spi.ManagedConnection#getLocalTransaction()
268 */
269 public LocalTransaction getLocalTransaction() throws ResourceException {
270 throw new NotSupportedException("");
271 }
272
273 /***
274 * {@inheritDoc}
275 *
276 * @see javax.resource.spi.ManagedConnection#getXAResource()
277 */
278 public XAResource getXAResource() throws ResourceException {
279 throw new NotSupportedException(RESOURCES.getString("method.not.supported.1", "getXAResource()"));
280 }
281
282 /***
283 * {@inheritDoc}
284 *
285 * @see javax.resource.spi.ManagedConnection#getMetaData()
286 */
287 public ManagedConnectionMetaData getMetaData() throws ResourceException {
288 return this;
289 }
290
291 /***
292 * {@inheritDoc}
293 *
294 * @see javax.resource.spi.ManagedConnection#getLogWriter()
295 */
296 public PrintWriter getLogWriter() throws ResourceException {
297 return monitor.getWriter();
298 }
299
300 /***
301 * {@inheritDoc}
302 *
303 * @see javax.resource.spi.ManagedConnection#setLogWriter(java.io.PrintWriter)
304 */
305 public void setLogWriter(final PrintWriter writer) throws ResourceException {
306 monitor.setWriter(writer);
307 }
308
309 /***
310 * {@inheritDoc}
311 *
312 * @see java.lang.Object#toString()
313 */
314 public String toString() {
315 return "[DomingoManagedConnection, todo]";
316 }
317
318 /***
319 * {@inheritDoc}
320 *
321 * @see javax.resource.spi.ManagedConnectionMetaData#getEISProductName()
322 */
323 public String getEISProductName() throws ResourceException {
324 return DomingoMetaData.EIS_PRODUCT_NAME;
325 }
326
327 /***
328 * {@inheritDoc}
329 *
330 * @see javax.resource.spi.ManagedConnectionMetaData#getEISProductVersion()
331 */
332 public String getEISProductVersion() throws ResourceException {
333 return session.getNotesVersion();
334 }
335
336 /***
337 * {@inheritDoc}
338 *
339 * @see javax.resource.spi.ManagedConnectionMetaData#getMaxConnections()
340 */
341 public int getMaxConnections() throws ResourceException {
342 return DomingoMetaData.MAX_CONNECTIONS;
343 }
344
345 /***
346 * {@inheritDoc}
347 *
348 * @return connection request information
349 */
350 public ConnectionRequestInfo getConnectionRequestInfo() {
351 return connectionRequestInfo;
352 }
353
354 /***
355 * {@inheritDoc}
356 *
357 * @see javax.resource.spi.ManagedConnectionMetaData#getUserName()
358 */
359 public String getUserName() throws ResourceException {
360 return session.getCanonicalUserName();
361 }
362
363 /***
364 * {@inheritDoc}
365 *
366 * @see javax.resource.spi.ManagedConnection#cleanup()
367 */
368 public void cleanup() throws ResourceException {
369 closeConnections();
370 }
371
372 /***
373 * Adds a new connection.
374 *
375 * @param connection the new connection.
376 */
377 public void addConnection(final Connection connection) {
378 if (connection == null) {
379 monitor.error(RESOURCES.getString("argument.is.null", "connection"));
380 return;
381 }
382 synchronized (connections) {
383 connections.add(connection);
384 }
385 }
386
387
388 /***
389 * Removes a connection from the set of connections associated with this managed connection.
390 *
391 * @param connection The connection to remove.
392 */
393 public void removeConnection(final DomingoConnection connection) {
394 if (connection == null) {
395 monitor.error(RESOURCES.getString("argument.is.null", "connection"));
396 return;
397 }
398 synchronized (connections) {
399 connections.remove(connection);
400 }
401 }
402
403 /***
404 * {@inheritDoc}
405 *
406 * @see javax.resource.spi.ManagedConnection#destroy()
407 */
408 public void destroy() throws ResourceException {
409 if (isDestroyed) {
410 return;
411 }
412 isDestroyed = true;
413 closeConnections();
414 try {
415 DNotesFactory.dispose(true);
416 } catch (DNotesException e) {
417 throw new ResourceException("Cannot dispose domingo factory ", e);
418 }
419 }
420
421 /***
422 * {@inheritDoc}
423 *
424 * @see javax.resource.spi.ManagedConnection#getConnection(javax.security.auth.Subject,
425 * javax.resource.spi.ConnectionRequestInfo)
426 */
427 public Object getConnection(final Subject subj, final ConnectionRequestInfo cri) throws ResourceException {
428 checkIfDestroyed();
429 boolean reauth = checkForReauthentication(subj, cri);
430 if (reauth) {
431 throw new ResourceException("reauthentication.not.supported");
432 } else {
433 final Connection connection = new DomingoConnectionImpl(this);
434 addConnection(connection);
435 return connection;
436 }
437 }
438
439 /***
440 * {@inheritDoc}
441 *
442 * Closes all connections associated with this managed connection.
443 *
444 * @throws ResourceException Failed to close one or more handles.
445 */
446 private void closeConnections() throws ResourceException {
447 final Iterator iterator = connections.iterator();
448 while (iterator.hasNext()) {
449 ((DomingoConnectionImpl) iterator.next()).close();
450 }
451 connections.clear();
452 }
453
454 /***
455 * {@inheritDoc}
456 *
457 * @see javax.resource.spi.ManagedConnection#addConnectionEventListener(javax.resource.spi.ConnectionEventListener)
458 */
459 public void addConnectionEventListener(final ConnectionEventListener connectionEventListener) {
460 if (connectionEventListener != null) {
461 synchronized (connectionEventListener) {
462 eventListeners.add(connectionEventListener);
463 }
464 }
465 }
466
467 /***
468 * {@inheritDoc}
469 *
470 * @see javax.resource.spi.ManagedConnection#removeConnectionEventListener(javax.resource.spi.ConnectionEventListener)
471 */
472 public void removeConnectionEventListener(final ConnectionEventListener connectionEventListener) {
473 if (connectionEventListener != null) {
474 synchronized (connectionEventListener) {
475 eventListeners.remove(connectionEventListener);
476 }
477 }
478 }
479
480 /***
481 * {@inheritDoc}
482 *
483 * @see javax.resource.spi.ConnectionEventListener#connectionClosed(javax.resource.spi.ConnectionEvent)
484 */
485 public void connectionClosed(final ConnectionEvent event) {
486 sendEvent(event);
487 }
488
489 /***
490 * {@inheritDoc}
491 *
492 * @see javax.resource.spi.ConnectionEventListener#localTransactionStarted(javax.resource.spi.ConnectionEvent)
493 */
494 public void localTransactionStarted(final ConnectionEvent event) {
495 sendEvent(event);
496 }
497
498 /***
499 * {@inheritDoc}
500 *
501 * @see javax.resource.spi.ConnectionEventListener#localTransactionCommitted(javax.resource.spi.ConnectionEvent)
502 */
503 public void localTransactionCommitted(final ConnectionEvent event) {
504 sendEvent(event);
505 }
506
507 /***
508 * {@inheritDoc}
509 *
510 * @see javax.resource.spi.ConnectionEventListener#localTransactionRolledback(javax.resource.spi.ConnectionEvent)
511 */
512 public void localTransactionRolledback(final ConnectionEvent event) {
513 sendEvent(event);
514 }
515
516 /***
517 * {@inheritDoc}
518 *
519 * @see javax.resource.spi.ConnectionEventListener#connectionErrorOccurred(javax.resource.spi.ConnectionEvent)
520 */
521 public void connectionErrorOccurred(final ConnectionEvent event) {
522 sendEvent(event);
523 }
524
525 /***
526 * Sends an event to all available event listeners.
527 *
528 * @param event the event to send
529 */
530 private void sendEvent(final ConnectionEvent event) {
531 final int type = event.getId();
532 ConnectionEventListener[] list = (ConnectionEventListener[]) eventListeners.toArray();
533 for (int i = 0; i < list.length; i++) {
534 switch (type) {
535 case ConnectionEvent.CONNECTION_CLOSED:
536 list[i].connectionClosed(event);
537 break;
538 case ConnectionEvent.LOCAL_TRANSACTION_STARTED:
539 list[i].localTransactionStarted(event);
540 break;
541 case ConnectionEvent.LOCAL_TRANSACTION_COMMITTED:
542 list[i].localTransactionCommitted(event);
543 break;
544 case ConnectionEvent.LOCAL_TRANSACTION_ROLLEDBACK:
545 list[i].localTransactionRolledback(event);
546 break;
547 case ConnectionEvent.CONNECTION_ERROR_OCCURRED:
548 list[i].connectionErrorOccurred(event);
549 break;
550 default:
551 throw new IllegalArgumentException(RESOURCES.getString("illegal.event.type", new Integer(type)));
552 }
553 }
554 }
555
556 private boolean checkForReauthentication(Subject subj, ConnectionRequestInfo cri) {
557 boolean reauth = false;
558 if (hasGenericCred) {
559 GenericCredential gc = extractGenericCredential(subj);
560 if (gc == null) {
561 reauth = true;
562 } else if (!gc.equals(genericCredential)) {
563 reauth = true;
564 }
565 } else if (hasPasswordCred) {
566 PasswordCredential pc = extractPasswordCredential(subj);
567 if (pc == null) {
568 reauth = true;
569 } else if (!pc.equals(passwordCredential)) {
570 reauth = true;
571 }
572 } else if (connectionRequestInfo != null && cri != null && !connectionRequestInfo.equals(cri)) {
573 reauth = true;
574 }
575 return reauth;
576 }
577
578 private GenericCredential extractGenericCredential(final Subject subj) {
579 GenericCredential gc = null;
580 Set theSet = subj.getPrivateCredentials(GenericCredential.class);
581 if (theSet.size() == 0) {
582 theSet = subj.getPublicCredentials(GenericCredential.class);
583 }
584 Iterator iter = theSet.iterator();
585 if (iter.hasNext()) {
586 gc = (GenericCredential) iter.next();
587 }
588 return gc;
589 }
590
591 private PasswordCredential extractPasswordCredential(final Subject subj) {
592 PasswordCredential pc = null;
593 Set theSet = subj.getPrivateCredentials(PasswordCredential.class);
594 for (Iterator iter = theSet.iterator(); iter.hasNext();) {
595 PasswordCredential temp = (PasswordCredential) iter.next();
596 if (temp.getManagedConnectionFactory().equals(managedConnectionFactory)) {
597 pc = temp;
598 break;
599 }
600 }
601 return pc;
602 }
603
604 private void checkIfDestroyed() throws IllegalStateException {
605 if (destroyed) {
606 throw new IllegalStateException(RESOURCES.getString("connection.already.destroyed"));
607 } else {
608 return;
609 }
610 }
611 }